#include <math.h>
#include "Cpu.h"
#include "common.h"

// used to start thread, thread function
void cpu_thread(void *_params) {
	struct cpu_params *params = (struct cpu_params*)_params;
	int history_count = 10;	// length of history counts to monitor for high cpu
	int *history = new int[history_count];
	int max_avg;
	int max_threads = 100;
	int min_threads = 10;
	int rate;	// rate to increase thread availability
	int sample = 0;

	
	// set initial thread count
	params->threads_allowed = min_threads;

	// assume an initially unavailable cpu level
	for(int ctr = 0; ctr < history_count; ctr ++) {
		history[ctr] = 75;
	}

	// keep thread going until the process ends
	while(!params->end) {
		while(params->isOn) {
			// get an update on the cpu level, this is period and takes about a second
			params->cpu = params->wmi.getCpuPercent();

			// adjust our history
			for(int ctr = 0; ctr < (history_count - 1); ctr ++) {
				history[ctr] = history[ctr + 1];
			}
			history[history_count - 1] = params->cpu;

			// get the max of the history
			max_avg = history[0];
			for(int ctr = 1; ctr < 3; ctr ++) {
				if(history[ctr] > max_avg) {
					max_avg = history[ctr];
				}
			}

			// adjust threads using max value of historical cpu average
			if(sample++ < history_count) {
				continue;	// don't adjust until we have a sample large as the history_count;
			}
			sample = 0;	// after this adjustment, we want a new sampling before adjusting

#ifdef _DEBUG
			char data[DEFAULT_BUFLEN];
/*			printf("max = (%d)", max_avg);
			for(int ctr = 0; ctr < history_count; ctr ++) {
				printf(", %d", history[ctr]);
			}
			printf("\n");
*/
#endif // _DEBUG
			if(max_avg < 75) {
				// calculate exact threads to reach 75 cpu, then div in half just to be safe
				rate = (int) floor((float)( ((75 * params->threads_allowed) / max_avg) / 2 ));
//				rate = 50 - max_avg;
				params->threads_allowed += rate;
				if(params->threads_allowed > max_threads) {
					params->threads_allowed = max_threads;
				}
#ifdef _DEBUG
//				printf("Adjusting thread count: %d\n", params->threads_allowed);
#endif // _DEBUG

				// now faking history for a bit, so we have a good average before adjusting
/*				for(int ctr = 0; ctr < history_count; ctr ++) {
					history[ctr] = 75;
				}
*/			}
			else if(max_avg < 90) {
				// just right, don't do anything, ideal range is 50 - 75
			}
			else {
				// reduce threads by 1/4th
				params->threads_allowed -= (int) ceil((float) params->threads_allowed / 4);
				//params->threads_allowed -= rate;
				if(params->threads_allowed < min_threads) {
					params->threads_allowed = min_threads;
				}
#ifdef _DEBUG
//				sprintf(data, "Adjusting thread count: %d\n", params->threads_allowed);	params->log->write(data);
#endif // _DEBUG

				// now faking history for a bit, so we have a good average before adjusting
/*				for(int ctr = 0; ctr < history_count; ctr ++) {
					history[ctr] = 75;
				}
*/			}
		}

		// put a sleep here to keep from maxing the cpu when paused
		Sleep(1000);	// sleep for a second
	}

	// free up allocated memory
	delete[] history;

	// end the thread
	_endthread();
}

// Constructor
Cpu::Cpu(Log *log) {
	params.wmi.init_local();
	params.cpu = 0;
	params.log = log;

	// switch on/off watching the cpu
	params.isOn = false;
	
	// if the thread is shutting down
	params.end = false;

	// launch our cpu watching thread
	_beginthread(cpu_thread, 0, &params);
}


// returns the last cpu average across all cores
int Cpu::getPercent() {
	return params.cpu;
}

// start watching the cpu average
void Cpu::start() {
	params.isOn = true;
}

// pause watching the cpu, when nothing is being processed
void Cpu::pause() {
	params.isOn = false;
	params.threads_allowed = 10;
}